Skip to contentMethod: setValue(ELContext, Object, Object, Object)
1: /*
2: * Licensed to the Apache Software Foundation (ASF) under one or more
3: * contributor license agreements. See the NOTICE file distributed with
4: * this work for additional information regarding copyright ownership.
5: * The ASF licenses this file to You under the Apache License, Version 2.0
6: * (the "License"); you may not use this file except in compliance with
7: * the License. You may obtain a copy of the License at
8: *
9: * http://www.apache.org/licenses/LICENSE-2.0
10: *
11: * Unless required by applicable law or agreed to in writing, software
12: * distributed under the License is distributed on an "AS IS" BASIS,
13: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14: * See the License for the specific language governing permissions and
15: * limitations under the License.
16: */
17: package jakarta.el;
18:
19: import static jakarta.el.ELUtil.getExceptionMessageString;
20:
21: import java.util.Objects;
22: import java.util.Optional;
23:
24: /**
25: * Defines property resolution behaviour on {@link Optional}s.
26: * <p>
27: * This resolver handles base objects that are instances of {@link Optional}.
28: * <p>
29: * If the {@link Optional#isEmpty()} is {@code true} for the base object and the property is {@code null} then the
30: * resulting value is {@code null}.
31: * <p>
32: * If the {@link Optional#isEmpty()} is {@code true} for the base object and the property is not {@code null} then the
33: * resulting value is the base object (an empty {@link Optional}).
34: * <p>
35: * If the {@link Optional#isPresent()} is {@code true} for the base object and the property is {@code null} then the
36: * resulting value is the result of calling {@link Optional#get()} on the base object.
37: * <p>
38: * If the {@link Optional#isPresent()} is {@code true} for the base object and the property is not {@code null} then the
39: * resulting value is the result of calling {@link ELResolver#getValue(ELContext, Object, Object)} using the
40: * {@link ELResolver} obtained from {@link ELContext#getELResolver()} with the following parameters:
41: * <ul>
42: * <li>The {@link ELContext} is the current context</li>
43: * <li>The base object is the result of calling {@link Optional#get()} on the current base object</li>
44: * <li>The property object is the current property object</li>
45: * </ul>
46: * <p>
47: * This resolver is always a read-only resolver.
48: */
49: public class OptionalELResolver extends ELResolver {
50:
51: @Override
52: public Object getValue(ELContext context, Object base, Object property) {
53: Objects.requireNonNull(context);
54:
55: if (base instanceof Optional) {
56: context.setPropertyResolved(base, property);
57: if (((Optional<?>) base).isEmpty()) {
58: if (property == null) {
59: return null;
60: } else {
61: return base;
62: }
63: } else {
64: if (property == null) {
65: return ((Optional<?>) base).get();
66: } else {
67: Object resolvedBase = ((Optional<?>) base).get();
68: return context.getELResolver().getValue(context, resolvedBase, property);
69: }
70: }
71: }
72:
73: return null;
74: }
75:
76:
77: /**
78: * {@inheritDoc}
79: * <p>
80: * If the base object is an {@link Optional} this method always returns {@code null} since instances of this
81: * resolver are always read-only.
82: */
83: @Override
84: public Class<?> getType(ELContext context, Object base, Object property) {
85: Objects.requireNonNull(context);
86:
87: if (base instanceof Optional) {
88: context.setPropertyResolved(base, property);
89: }
90:
91: return null;
92: }
93:
94:
95: /**
96: * {@inheritDoc}
97: * <p>
98: * If the base object is an {@link Optional} this method always throws a {@link PropertyNotWritableException} since
99: * instances of this resolver are always read-only.
100: */
101: @Override
102: public void setValue(ELContext context, Object base, Object property, Object value) {
103: Objects.requireNonNull(context);
104:
105:• if (base instanceof Optional) {
106: throw new PropertyNotWritableException(getExceptionMessageString(
107: context, "resolverNotwritable", new Object[] { base.getClass().getName() }));
108: }
109: }
110:
111:
112: /**
113: * {@inheritDoc}
114: * <p>
115: * If the base object is an {@link Optional} this method always returns {@code true} since instances of this
116: * resolver are always read-only.
117: */
118: @Override
119: public boolean isReadOnly(ELContext context, Object base, Object property) {
120: Objects.requireNonNull(context);
121:
122: if (base instanceof Optional) {
123: context.setPropertyResolved(base, property);
124: return true;
125: }
126:
127: return false;
128: }
129:
130:
131: /**
132: * {@inheritDoc}
133: * <p>
134: * If the base object is an {@link Optional} this method always returns {@code Object.class}.
135: */
136: @Override
137: public Class<?> getCommonPropertyType(ELContext context, Object base) {
138: if (base instanceof Optional) {
139: return Object.class;
140: }
141:
142: return null;
143: }
144:
145:
146: @Override
147: public <T> T convertToType(ELContext context, Object obj, Class<T> type) {
148: Objects.requireNonNull(context);
149: if (obj instanceof Optional) {
150: if (((Optional<?>) obj).isPresent()) {
151: Object value = ((Optional<?>) obj).get();
152: // If the value is assignable to the required type, do so.
153: if (type.isAssignableFrom(value.getClass())) {
154: context.setPropertyResolved(true);
155: @SuppressWarnings("unchecked")
156: T result = (T) value;
157: return result;
158: }
159:
160: try {
161: Object convertedValue = context.convertToType(value, type);
162: context.setPropertyResolved(true);
163: @SuppressWarnings("unchecked")
164: T result = (T) convertedValue;
165: return result;
166: } catch (ELException e) {
167: /*
168: * TODO: This isn't pretty but it works. Significant refactoring would be required to avoid the
169: * exception. See also ELUtil.isCoercibleFrom().
170: */
171: }
172: } else {
173: context.setPropertyResolved(true);
174: return null;
175: }
176: }
177: return null;
178: }
179: }